﻿/****************************************************************************
** file: convert.cpp
** brief: cv::Mat 与 QImage 之间的转换函数。在 Qt 程序中使用 OpenCV 时有用。
** Copyright (C) LiYuan
** Author: LiYuan
** E-Mail: 18069211#qq(.)com
** Version 2.0.1
** Last modified: 2019.01.11
** Modified By: LiYuan
****************************************************************************/

#include "convert.h"

bool colorImage2GrayImage(const cv::Mat &inImage, cv::Mat &outImage)
{
    switch (inImage.channels())
    {
    case 1:
        outImage = inImage.clone();
        break;
    case 3:
        cv::cvtColor(inImage, outImage, cv::COLOR_RGB2GRAY);
        return true;
    case 4:
        cv::cvtColor(inImage, outImage, cv::COLOR_RGBA2GRAY);
        return true;
    default:
        break;
    }
    return false;
}

QImage cvMat2QImage(const cv::Mat& mat, bool clone, bool rb_swap)
{
    const uchar *pSrc = (const uchar*)mat.data;
    // 8-bits unsigned, NO. OF CHANNELS = 1
    if(mat.type() == CV_8UC1)
    {
        //QImage image(mat.cols, mat.rows, QImage::Format_Grayscale8);
        QImage image(pSrc, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_Grayscale8);
        if(clone) return image.copy();
        return image;
    }
    // 8-bits unsigned, NO. OF CHANNELS = 3
    else if(mat.type() == CV_8UC3)
    {
        // Create QImage with same dimensions as input Mat
        QImage image(pSrc, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_RGB888);
        if(clone)
        {
            if(rb_swap) return image.rgbSwapped();
            return image.copy();
        }
        else
        {
            if(rb_swap)
            {
                cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
            }
            return image;
        }

    }
    else if(mat.type() == CV_8UC4)
    {
        qDebug() << "CV_8UC4";
        QImage image(pSrc, mat.cols, mat.rows, static_cast<int>(mat.step), QImage::Format_ARGB32);
        if(clone) return image.copy();
        return image;
    }
    else
    {
        qDebug() << "ERROR: Mat could not be converted to QImage.";
        return QImage();
    }
}

cv::Mat QImage2cvMat(const QImage &image, bool clone, bool rb_swap)
{
    cv::Mat mat;
    //qDebug() << image.format();
    switch(image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_RGB32:
    case QImage::Format_ARGB32_Premultiplied:
        mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void *)(image.bits()), static_cast<int>(image.bytesPerLine()));
        if(clone)  mat = mat.clone();
        break;
    case QImage::Format_RGB888:
        mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void *)image.bits(), static_cast<int>(image.bytesPerLine()));
        if(clone)  mat = mat.clone();
        if(rb_swap) cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
        break;
    case QImage::Format_Indexed8:
    case QImage::Format_Grayscale8:
        mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void *)image.bits(), image.bytesPerLine());
        if(clone)  mat = mat.clone();
        break;
    default:
        break;
    }
    return mat;
}

bool extractPoints(const cv::Mat &grayEdgeImage, std::vector<cv::Point2i> &points, cv::Rect roi)
{
    if(grayEdgeImage.channels() != 1) return false;
    if(roi.empty())
    {
        roi.width = grayEdgeImage.cols;
        roi.height = grayEdgeImage.rows;
        roi.x = 0;
        roi.y = 0;
    }
    else
    {
        if(roi.x < 0) roi.x = 0;
        if(roi.y < 0) roi.y = 0;
        if(roi.width > grayEdgeImage.cols - roi.x) roi.width = grayEdgeImage.cols - roi.x;
        if(roi.height > grayEdgeImage.rows - roi.y) roi.height = grayEdgeImage.rows - roi.y;
    }
    cv::Mat edge = grayEdgeImage(roi);

    points.clear();
    for(int row = 0; row < edge.rows; row++)
    {
        const uchar * pLine = edge.ptr<const uchar>(row);
        for(int col = 0; col < edge.cols; col++)
        {
            if(pLine[col] > 0)
            {
                points.push_back(cv::Point2i(col + roi.x, row + roi.y));
            }
        }
    }
    return points.size();
}


QDebug operator<<(QDebug dbg, const cv::Point &p)
{
  QDebugStateSaver saver(dbg);
  dbg.nospace() << "cv::Point(" << p.x << ", " << p.y << ")";
  return dbg;
}

QDebug operator<<(QDebug dbg, const cv::Point2d &p)
{
  QDebugStateSaver saver(dbg);
  dbg.nospace() << "cv::Point2d(" << p.x << ", " << p.y << ")";
  return dbg;
}

QDebug operator<<(QDebug dbg, const cv::Point2f &p)
{
  QDebugStateSaver saver(dbg);
  dbg.nospace() << "cv::Point2f(" << p.x << ", " << p.y << ")";
  return dbg;
}


QDebug operator<<(QDebug dbg, const cv::Point2l &p)
{
  QDebugStateSaver saver(dbg);
  dbg.nospace() << "cv::Point2l(" << p.x << ", " << p.y << ")";
  return dbg;
}

QDebug operator<<(QDebug dbg, const cv::Size &s)
{
    QDebugStateSaver saver(dbg);
    dbg.nospace() << "cv::Size(width: " << s.width << ", height: " << s.height << ")";
    return dbg;
}

QDebug operator<<(QDebug dbg, const cv::Scalar &s)
{
    QDebugStateSaver saver(dbg);
    dbg.nospace() << "cv::Scalar(Red: " << s[3] << ", Green: " << s[1] << ", Blue: " << s[0] << ")";
    return dbg;
}

QDebug operator<<(QDebug dbg, const cv::Rect &r)
{
    QDebugStateSaver saver(dbg);
    dbg.nospace() << "cv::Rect(x: " << r.x << ", y: " << r.y << ", Width: " << r.width << ", Height: " << r.height << ")";
    return dbg;
}

QDebug operator<<(QDebug dbg, const cv::Matx22d &m)
{
    QDebugStateSaver saver(dbg);
    dbg.nospace() << "cv::Mat2d((" << m(0, 0) << ", " << m(0, 1) << "),(" << m(1, 0) << ", " << m(1, 1) << "))" ;
    return dbg;
}
